package com.caocaog.market.finance.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.caocaog.market.data.transfer.entity.MkSiteBasic;
import com.caocaog.market.data.transfer.service.MkSiteBasicService;
import com.caocaog.market.finance.entity.*;
import com.caocaog.market.finance.enums.WithdrawStatus;
import com.caocaog.market.finance.enums.WithdrawType;
import com.caocaog.market.finance.mapper.FinanceGroupMapper;
import com.caocaog.market.finance.service.*;
import com.caocaog.market.merchant.entity.MkMerchantBasic;
import com.caocaog.market.merchant.service.MkMerchantService;
import com.caocaog.market.service.IShopBasicService;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.caocaog.market.finance.entity.FinanceGroup.isDeletable;
import static com.caocaog.market.finance.enums.WithdrawStatus.*;
import static com.caocaog.market.finance.enums.WithdrawType.MERCHANT;
import static com.caocaog.market.finance.enums.WithdrawType.SITE;

@Service
@RequiredArgsConstructor
public class FinanceGroupServiceImpl extends ServiceImpl<FinanceGroupMapper, FinanceGroup> implements FinanceGroupService {

    private final MkSiteBasicService siteService;
    private final MkMerchantService merchantService;
    private final FinanceLineService financeLineService;
    private final FinanceGroupLineMergeService financeGroupLineMergeService;


    @Override
    public IPage<FinanceGroup> queryFinanceGroupPage(Page<FinanceGroup> page, FinanceGroup financeGroup) {
        // 基础查询条件
        LambdaQueryWrapper<FinanceGroup> queryWrapper = new LambdaQueryWrapper<FinanceGroup>()
                .eq(StringUtils.isNotBlank(financeGroup.getShopId()), FinanceGroup::getShopId, financeGroup.getShopId())
                .eq(financeGroup.getStatus() != null, FinanceGroup::getStatus, financeGroup.getStatus())
                .likeRight(StringUtils.isNotBlank(financeGroup.getGroupNo()), FinanceGroup::getGroupNo, financeGroup.getGroupNo())
                .like(StringUtils.isNotBlank(financeGroup.getAlipayAccount()), FinanceGroup::getAlipayAccount, financeGroup.getAlipayAccount())
                .like(StringUtils.isNotBlank(financeGroup.getAlipayName()), FinanceGroup::getAlipayName, financeGroup.getAlipayName())
                .like(StringUtils.isNotBlank(financeGroup.getAlipayNo()), FinanceGroup::getAlipayNo, financeGroup.getAlipayNo());

        // 申请时间范围
        if (StringUtils.isNotBlank(financeGroup.getApplicationTimeFrom()) && StringUtils.isNotBlank(financeGroup.getApplicationTimeTo())) {
            if (financeGroup.getApplicationTimeFrom().equals(financeGroup.getApplicationTimeTo()))
                queryWrapper.apply("DATE_FORMAT(application_time,'%Y-%m-%d') = '" + financeGroup.getApplicationTimeFrom() + "'");
            else queryWrapper
                    .ge(FinanceGroup::getApplicationTime, financeGroup.getApplicationTimeFrom())
                    .le(FinanceGroup::getApplicationTime, financeGroup.getApplicationTimeTo());
        }

        // 提现组类型
        if (financeGroup.getType() == MERCHANT) queryWrapper
                .eq(FinanceGroup::getType, MERCHANT)
                .eq(FinanceGroup::getMerchantId, financeGroup.getMerchantId());
        else if (financeGroup.getType() == SITE) queryWrapper
                .eq(FinanceGroup::getType, SITE)
                .eq(FinanceGroup::getSiteId, financeGroup.getSiteId());

        baseMapper.queryFinanceGroupPage(page, queryWrapper, financeGroup.getOrderNo(), financeGroup.getSiteName());
        for (FinanceGroup item : page.getRecords())
            item.setFinanceLines(financeLineService.getFinanceLineListByGroupId(item.getId())).setDeletable();

        return page;
    }


    @Override
    public FinanceGroup getFinanceGroupById(Long id) {
        FinanceGroup financeGroup = Optional.ofNullable(baseMapper.selectById(id))
                .orElseThrow(() -> new RuntimeException("提现组不存在"));

        List<FinanceLine> financeLineList = financeLineService.getFinanceLineListByGroupId(id);
        Assert.isTrue(!financeLineList.isEmpty(), "提现组下明细不存在");

        return financeGroup.setFinanceLines(financeLineList);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void applyWithdrawal(List<Long> lineIds, WithdrawType type, String siteId, String merchantId) {
        Assert.isTrue(!lineIds.isEmpty(), "请选择至少一条提现明细");
        Assert.isTrue(lineIds.size() <= MAX_WITHDRAW_COUNT, String.format("单次最多提现%s条明细，当前为「%s」条", MAX_WITHDRAW_COUNT, lineIds.size()));
        FinanceGroup financeGroup = new FinanceGroup().generateGroupNo().setType(type);

        // 校验集合内部提现行状态
        Boolean checkResult = financeLineService.checkFinanceLineUnWithdrawCount(lineIds, type);
        Assert.isTrue(checkResult, "提现明细中存在已提现的记录");

        if (type == MERCHANT) {
            // 校验提现行集内部是否出自同一店铺
            List<String> shopIds = financeLineService.getFinanceLineShopIds(lineIds);
            Assert.isTrue(!shopIds.isEmpty(), "提现明细异常，未查询到店铺主键信息");
            Assert.isTrue(shopIds.size() == 1, "提现明细中存在不同店铺的记录");
            financeGroup.setShopId(shopIds.get(0));
        }

        // 更新提现行状态
        Integer successCount = financeLineService.updateFinanceLineStatus(lineIds, type, TO_BE_REVIEWED);
        Assert.isTrue(successCount == lineIds.size(), "更新提现行状态失败");

        // 设置提现支付宝信息
        if (type == MERCHANT) {
            MkMerchantBasic merchant = Optional.ofNullable(merchantService.getById(merchantId)).orElseThrow(() -> new RuntimeException("商户不存在"));
            Assert.isTrue(StringUtils.isNotBlank(merchant.getAlipay()) && StringUtils.isNotBlank(merchant.getAliname()), "商户未设置提现支付宝信息");
            financeGroup.setAlipayAccount(merchant.getAlipay()).setAlipayName(merchant.getAliname()).setMerchantId(merchantId);
        } else if (type == SITE) {
            MkSiteBasic site = Optional.ofNullable(siteService.getById(siteId)).orElseThrow(() -> new RuntimeException("分站不存在"));
            Assert.isTrue(StringUtils.isNotBlank(site.getMobile()) && StringUtils.isNotBlank(site.getContactPerson()), "站点未设置提现支付宝信息");
            financeGroup.setAlipayAccount(site.getMobile()).setAlipayName(site.getContactPerson()).setSiteId(siteId);
        } else throw new RuntimeException("提现类型异常");

        // 创建提现组
        baseMapper.insert(financeGroup);
        Long groupId = financeGroup.getId();
        Assert.notNull(groupId, "创建提现组失败");

        // 合并提现行与提现组
        Boolean mergeResult = financeGroupLineMergeService.mergeFinanceGroupLine(groupId, lineIds);
        Assert.isTrue(mergeResult, "合并提现行与提现组失败");
    }


    @Override
    public Integer updateFinanceGroup(FinanceGroup financeGroup) {
        return baseMapper.update(financeGroup.clearFields(), new LambdaUpdateWrapper<FinanceGroup>()
                .set(FinanceGroup::getRejectReason, financeGroup.getRejectReason())
                .eq(FinanceGroup::getId, financeGroup.getId()));
    }


    @Override
    public Integer deleteFinanceGroup(Long id) {
        FinanceGroup financeGroup = getFinanceGroupById(id);
        Assert.isTrue(isDeletable(financeGroup), "已提现的提现组不可删除");

        // 更新提现行状态为「待提现」PENDING_WITHDRAWAL
        List<Long> financeLineIds = financeGroup.getFinanceLines().stream().map(FinanceLine::getId).collect(Collectors.toList());
        financeLineService.updateFinanceLineStatus(financeLineIds, financeGroup.getType(), PENDING_WITHDRAWAL, id);

        // 删除提现组
        return baseMapper.deleteById(id);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer auditFinanceGroup(Long id, WithdrawStatus status, @Nullable String auditRemark) {
        FinanceGroup financeGroup = getFinanceGroupById(id);
        WithdrawType type = financeGroup.getType();

        // 更改提现集状态
        Integer changeCount = changeFinanceGroupStatus(financeGroup, status, financeGroup.getStatus(), auditRemark);
        if (changeCount > 0) {
            // 更新提现行状态
            List<Long> financeLineIds = financeGroup.getFinanceLines().stream().map(FinanceLine::getId).collect(Collectors.toList());
            WithdrawStatus lineStatus = status == PENDING_TRANSFER ? PENDING_TRANSFER : PENDING_WITHDRAWAL; // 如果审核驳回，订单行状态改为「待提现」PENDING_WITHDRAWAL
            financeLineService.updateFinanceLineStatus(financeLineIds, type, lineStatus, id);
        }

        return changeCount;
    }


    /**
     * 更改提现集状态
     */
    private Integer changeFinanceGroupStatus(FinanceGroup financeGroup, WithdrawStatus status, @Nullable WithdrawStatus lastStatus, @Nullable String auditRemark) {
        if (lastStatus == null) lastStatus = Optional.ofNullable(baseMapper.selectById(financeGroup.getId()))
                .orElseThrow(() -> new RuntimeException("提现组不存在")).getStatus();
        Assert.isTrue(status.canUpdate(lastStatus), "审核状态不合法，当前状态为：" + lastStatus);

        financeGroup.setStatus(status);
        if (status == REJECT || status == TRANSFER_FAILED || status == FULLY_WITHDRAW) financeGroup.setRejectReason(auditRemark);
        if (status == REJECT || status == PENDING_TRANSFER) financeGroup.setAuditTime(LocalDateTime.now());
        if (status == TRANSFER_FAILED || status == FULLY_WITHDRAW) financeGroup.setTransferTime(LocalDateTime.now());

        return updateFinanceGroup(financeGroup);
    }


    @Override
    @Nullable
    @Transactional(rollbackFor = Exception.class)
    public String transferFinanceGroup(Long id) {
        FinanceGroup financeGroup = getFinanceGroupById(id);
        WithdrawType type = financeGroup.getType();
        WithdrawStatus lastStatus = financeGroup.getStatus();
        Assert.isTrue(FULLY_WITHDRAW.canUpdate(lastStatus) && TRANSFER_FAILED.canUpdate(lastStatus), "审核状态不合法");
        List<Long> financeLineIds = financeGroup.getFinanceLines().stream().map(FinanceLine::getId).collect(Collectors.toList());


        // TODO 转账

        Boolean isTransferSuccess = true;
        financeGroup.setAlipayNo("1234567890").setAlipayCallbackInfo("{test}");
        String errorMsg = null;

        if (isTransferSuccess) { // 转账成功
            changeFinanceGroupStatus(financeGroup, FULLY_WITHDRAW, lastStatus, null);
            financeLineService.updateFinanceLineStatus(financeLineIds, type, FULLY_WITHDRAW, id);
        } else {
            changeFinanceGroupStatus(financeGroup, TRANSFER_FAILED, lastStatus, errorMsg);
            // 转账失败，不修改订单行的状态，还是「待转账」PENDING_TRANSFER
        }

        return errorMsg;
    }


    public WithdrawStatus getStatus(int withdrawCashStatus) {
        switch (withdrawCashStatus) {
            case 1:
            case 4:
            case 6:
                return PENDING_WITHDRAWAL;
            case 2:
                return TO_BE_REVIEWED;
            case 3:
                return PENDING_TRANSFER;
            case 5:
                return FULLY_WITHDRAW;
            default:
                throw new RuntimeException("未知的提现状态");
        }
    }


    @Setter(onMethod_ = @Autowired)
    private MkBillLineService billLineService;
    @Setter(onMethod_ = @Autowired)
    private MkBillBaseService billBaseService;
    @Setter(onMethod_ = @Autowired)
    private IMkBillChangeService billChangeService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void dataTransfer() {
        // 删除所有新表全部数据
        baseMapper.deleteAllFinance();

        // 查询重复数据的订单号
        List<String> duplicateDataOrderNoList = baseMapper.queryDuplicateData().stream().map(MkBillLine::getOrderNo).collect(Collectors.toList());

        if (!duplicateDataOrderNoList.isEmpty()) {
            List<MkBillLine> duplicateDataList = billLineService.getBaseMapper().selectList(new LambdaQueryWrapper<MkBillLine>().in(MkBillLine::getOrderNo, duplicateDataOrderNoList));
            System.out.println(duplicateDataList.size());

            // 对重复数据进行分组处理 key: orderNo, value: billLineList
            Map<String, List<MkBillLine>> listMap = duplicateDataList.stream().collect(Collectors.groupingBy(MkBillLine::getOrderNo, Collectors.toList()));
            System.out.println(listMap.size());
            List<String> toDoDeleteBillLineIds = new ArrayList<>();
            for (Map.Entry<String, List<MkBillLine>> entry : listMap.entrySet()) {
                System.out.println("key: " + entry.getKey() + ", value size: " + entry.getValue().size());
                toDoDeleteBillLineIds.addAll(entry.getValue().subList(3, entry.getValue().size()).stream().map(MkBillLine::getId).collect(Collectors.toList()));
            }

            // 删除重复line数据
            billLineService.getBaseMapper().deleteBatchIds(toDoDeleteBillLineIds);
        }

        // 查询base旧表数据
        List<MkBillBase> allBillBaseList = billBaseService.getBaseMapper().selectList(new LambdaQueryWrapper<MkBillBase>().in(MkBillBase::getPayStatus, 1, 2));
        Map<String, MkBillBase> allBillBaseListMap = allBillBaseList.stream().collect(Collectors.toMap(MkBillBase::getOrderNo, Function.identity()));
        System.out.println("旧base表数据量：" + allBillBaseList.size());
        System.out.println("根据orderNo转为Map后数据量：" + allBillBaseListMap.size());


        // 查询line旧表数据
        List<MkBillLine> allBillLineList = billLineService.getBaseMapper().selectList(null);
        Map<String, List<MkBillLine>> allBillLineListMap = allBillLineList.stream().collect(Collectors.groupingBy(MkBillLine::getOrderNo, Collectors.toList()));
        System.out.println("旧line表数据量：" + allBillLineList.size());
        System.out.println("根据orderNo分组后数据量：" + allBillLineListMap.size());

        List<FinanceGroupLineMerge> toDoInsertFinanceMergeList = new ArrayList<>();

        ZoneId zoneId = ZoneId.systemDefault();
        List<FinanceLine> toDoInsertFinanceLines = new ArrayList<>();
        long i = 0;
        for (Map.Entry<String, MkBillBase> entry : allBillBaseListMap.entrySet()) {
            String orderNo = entry.getKey();
            List<MkBillLine> values = allBillLineListMap.get(orderNo);
            MkBillBase billBase = entry.getValue();
            BigDecimal systemPCT = null;
            BigDecimal sitePCT = null;
            Long id = ++i;

            WithdrawStatus siteStatus = PENDING_WITHDRAWAL;
            WithdrawStatus shopStatus = PENDING_WITHDRAWAL;

            if (values != null && !values.isEmpty())
                for (MkBillLine value : values) {
                    if (value.getBillChangeId() != null && value.getWithdrawCashStatus() != 4)
                        toDoInsertFinanceMergeList.add(new FinanceGroupLineMerge().setLineId(id).setGroupId(Long.valueOf(value.getBillChangeId())));

                    if (value.getType() == 1) {
                        systemPCT = new BigDecimal(value.getRakeRate());
                    } else if (value.getType() == 2) {
                        sitePCT = new BigDecimal(value.getRakeRate());
                        siteStatus = getStatus(value.getWithdrawCashStatus());
                    } else if (value.getType() == 3) {
                        shopStatus = getStatus(value.getWithdrawCashStatus());
                    } else throw new RuntimeException("未知line类型");
                }


            toDoInsertFinanceLines.add(new FinanceLine()
                    .setId(id)
                    .setCreateTime(billBase.getCreateTime().toInstant().atZone(zoneId).toLocalDateTime())
                    .setUpdateTime(billBase.getUpdateTime() == null ? null : billBase.getUpdateTime().toInstant().atZone(zoneId).toLocalDateTime())
                    .setSystemPCT(systemPCT)
                    .setSitePCT(sitePCT)
                    .calculateShopPCT()
                    .setSiteStatus(siteStatus)
                    .setShopStatus(shopStatus)
                    .setOrderNo(orderNo)
                    .setPayTime(billBase.getPayTime().toInstant().atZone(zoneId).toLocalDateTime())
                    .setPayAmount(new BigDecimal(billBase.getPayAmount()))
                    .setSettlement(billBase.getPayStatus() == 2)
            );

        }

        System.out.println("待插入financeGroupLineMerge数据量：" + toDoInsertFinanceMergeList.size());

        System.out.println("待插入financeLine数量: " + toDoInsertFinanceLines.size());
        System.out.println("前10条数据：");
        for (FinanceLine financeLine : toDoInsertFinanceLines.subList(0, 10)) {
            System.out.println(financeLine);
        }

        // 查询change旧数据
        List<MkBillChange> billChanges = billChangeService.getBaseMapper().selectList(new LambdaQueryWrapper<MkBillChange>().notIn(MkBillChange::getWithdrawCashStatus, 4));
        System.out.println("旧change表数据量：" + billChanges.size());


        Map<Integer, List<MkBillLine>> allBillLineListByChangeIdMap = allBillLineList.stream()
                .filter(item -> item.getBillChangeId() != null)
                .collect(Collectors.groupingBy(MkBillLine::getBillChangeId));

        System.out.println("根据changeId分组后数据量：" + allBillLineListByChangeIdMap.size());


        List<FinanceGroup> toDoInsertFinanceGroups = new ArrayList<>();
        for (MkBillChange billChange : billChanges) {
            if (!allBillLineListByChangeIdMap.containsKey(billChange.getId())) {
                System.out.println(billChange.getId());
                continue;
            }

            WithdrawStatus status;
            switch (billChange.getWithdrawCashStatus()) {
                case 2:
                    status = WithdrawStatus.TO_BE_REVIEWED;
                    break;
                case 3:
                    status = WithdrawStatus.PENDING_TRANSFER;
                    break;
                case 4:
                    status = WithdrawStatus.REJECT;
                    break;
                case 5:
                    status = WithdrawStatus.FULLY_WITHDRAW;
                    break;
                default:
                    throw new RuntimeException("未知的提现状态");
            }

            List<MkBillLine> billLines = allBillLineListByChangeIdMap.get(billChange.getId());

            toDoInsertFinanceGroups.add(new FinanceGroup()
                    .setId(Long.valueOf(billChange.getId()))
                    .setGroupNo(billChange.getChangeNo())
                    .setSiteId(billChange.getSiteId())
                    .setMerchantId(billChange.getMerchantId())
                    .setShopId(billChange.getShopName())
                    .setCreateTime(billChange.getCreateTime().toInstant().atZone(zoneId).toLocalDateTime())
                    .setUpdateTime(billChange.getUpdateTime() == null ? null : billChange.getUpdateTime().toInstant().atZone(zoneId).toLocalDateTime())
                    .setApplicationTime(billChange.getCreateTime().toInstant().atZone(zoneId).toLocalDateTime())
                    .setType(StringUtils.isBlank(billChange.getSiteId()) ? MERCHANT : SITE)
                    .setAlipayAccount(billLines.get(0).getRakeAlipay())
                    .setAlipayName(billLines.get(0).getRakeAliname())
                    .setStatus(status)
            );
        }


        financeLineService.saveBatch(toDoInsertFinanceLines);
        financeGroupLineMergeService.saveBatch(toDoInsertFinanceMergeList);
        saveBatch(toDoInsertFinanceGroups);

    }

    @Setter(onMethod_ = @Autowired)
    public MkSiteBasicService siteBasicService;

    @Setter(onMethod_ = @Autowired)
    public IShopBasicService shopBasicService;

    @Override
    public void updateLine() {
        List<FinanceLine> financeLines = baseMapper.getFinanceLineListIsNullSystemPCT();
        for (FinanceLine financeLine : financeLines) {

            // 获取总部抽成比例
            BigDecimal systemPCT = new BigDecimal(Optional.ofNullable(Optional.ofNullable(siteBasicService.getById(financeLine.getMerchantId()))
                    .orElseThrow(() -> new RuntimeException("分站不存在"))
                    .getSysRakeRate()).orElse("1")).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);

            // 获取分站抽成比例
            BigDecimal sitePCT = new BigDecimal(Optional.ofNullable(Optional.ofNullable(shopBasicService.getById(financeLine.getShopId()))
                    .orElseThrow(() -> new RuntimeException("店铺不存在"))
                    .getCommissionRate()).orElse("0"));

            financeLine
                    .setSystemPCT(systemPCT)
                    .setSitePCT(sitePCT)
                    .calculateShopPCT();
        }

        for (FinanceLine financeLine : financeLines) {
            System.out.printf("id: %s, systemPCT: %s, sitePCT: %s, shopPCT: %s\n", financeLine.getId(), financeLine.getSystemPCT(), financeLine.getSitePCT(), financeLine.getShopPCT());
        }

        financeLineService.updateBatchById(financeLines);

    }


}